package org.ovhoo.zonfig;

import java.util.ArrayList;
import java.util.Iterator;

import org.ovhoo.io.ZBufferedWriter;

/**
 * type of data in the xml configuration file
 * @author mzalim
 *
 */
public class Type {
	
	public enum typesEnum{intType, unsigned , floatType, string, specified };
	private String name;
	private typesEnum base;
	
	private ArrayList<Value> values;
	private ArrayList<Value> forbidden;
	
	
	private int minInt=-2147483648, maxInt=2147483647;
	private boolean minimal=false, maximal=false; //indicate if aminimal and a maximal value was specified
	private double minDouble, maxDouble;
	private long minLength=0, maxLength=0, length=0;
	
	/**
	 * default constructor
	 * @param name : the name of this type
	 * @param base : the base type
	 */
	public Type(String name, String type){
		this.name = name;
		this.base = stringToType(type);
		
		this.values = new ArrayList<Value>();
		this.forbidden = new ArrayList<Value>();
	}
	
	/**
	 * default constructor
	 * @param name : the name of this type
	 * @param base : the base type
	 */
	public Type(String name, typesEnum type){
		this.name = name;
		this.base = type;
		
		this.values = new ArrayList<Value>();
		this.forbidden = new ArrayList<Value>();
	}
	
	//geters and seters
	/**
	 * return the Type name
	 * @return the type name as string
	 */
	public String getName(){
		return this.name;
	}
	
	/**
	 * type name seter
	 * @param name : the new type name
	 */
	public void setName(String name){
		this.name = name;
	}
	
	/**
	 * return the Type base
	 * @return the type base as string
	 */
	public String getStringBase(){
		return typeToString(this.base);
	}
	
	/**
	 * return the Type base
	 * @return the type base
	 */
	public typesEnum getBase(){
		return this.base;
	}
	
	/**
	 * type base seter
	 * @param name : the new type base as string
	 */
	public void setBase(String type){
		this.base = stringToType(type);
	}
	
	/**
	 * type base seter
	 * @param name : the new type base
	 */
	public void setBase(typesEnum type){
		this.base = type;
	}
	
	
	
	
	
	/**
	 * return minimal integer value
	 * for integer based type
	 * @return minimal integer value
	 */
	public int getMinInt(){
		return this.minInt;
	}
	
	/**
	 * return maximal integer value
	 * for integer based type
	 * @return maximal integer value
	 */
	public int getMaxInt(){
		return this.maxInt;
	}
	
	/**
	 * return minimal double value
	 * for float based type
	 * @return minimal double value
	 */
	public double getMinDouble(){
		return this.minDouble;
	}
	
	/**
	 * return minimal double value
	 * for float based type
	 * @return minimal double value
	 */
	public double getMaxDouble(){
		return this.maxDouble;
	}
	
	/**
	 * return the minimal length of a string based type
	 * @return minimal length of the string
	 */
	public long getMinLength(){
		return this.minLength;
	}
	
	/**
	 * return the maximal length of a string based type
	 * @return maximal length of the string
	 */
	public long getMaxLength(){
		return this.maxLength;
	}
	
	/**
	 * return the length of a string based type
	 * @return length of the string
	 */
	public long getlength(){
		return this.length;
	}
	
	
	/**
	 * change the minimal integer based type value
	 * @param minInt : the minimal value
	 */
	protected void setMinInt(int minInt){
		this.minInt = minInt;
	}
	
	/**
	 * change the maximal integer based type value
	 * @param maxInt : the maximal value
	 */
	protected void setMaxInt(int maxInt){
		this.maxInt = maxInt;
	}
	
	/**
	 * change the minimal double based type value
	 * @param minInt : the minimal value
	 */
	protected void setMinDouble(double mindouble){
		this.minDouble = mindouble;
	}
	
	/**
	 * change the maximal double based type value
	 * @param maxInt : the maximal value
	 */
	protected void setMaxDouble(double maxDouble){
		this.maxDouble = maxDouble;
	}
	
	/**
	 * Set the max value
	 * @param value : the new maximal value
	 */
	public void setMax(int value){
			this.setMaxInt(value);
			this.setMaxDouble(value);
			this.maximal = true;
	}
	
	/**
	 * set maximal value
	 * @param maxInt maximal integer value as string
	 * @param maxDouble maximal float value as string
	 */
	public void setMax(String maxInt, String maxDouble){
		if ((maxInt == null) && (maxDouble == null)){
			return;
		}
		else if ((maxInt != null) && (maxDouble != null)){
			
			double _doubleValue = Double.valueOf(maxDouble);
			int _intValue = Integer.valueOf(maxInt);
			if (this.base == typesEnum.floatType){
				this.setMax(_doubleValue);
			}
			else if ((this.base == typesEnum.intType) || (this.base == typesEnum.unsigned)){
				this.setMax(_intValue);
			}
			else{
				if (_intValue > _doubleValue) this.setMax(_intValue);
				else this.setMax(_doubleValue);
			}
		}
		else if (maxInt == null){
			double _doubleValue = Double.valueOf(maxDouble);
			this.setMax(_doubleValue);
		}
		else if (maxDouble == null){
			int _intValue = Integer.valueOf(maxInt);
			this.setMax(_intValue);
		}
		else{
			System.err.println("Type::setMax(string, string) : Impossible case");
		}
		
	}
	
	/**
	 * Set the max value
	 * @param value : the new maximal value
	 */
	public void setMax(double value){
		if (this.base == typesEnum.floatType){
			int _intValue = (int) value;
			if (_intValue < value) _intValue++;
			this.setMaxDouble(value);
			this.setMaxInt(_intValue);
		}
		else{
			this.setMax((int)value);
		}
		this.maximal = true;
	}
	
	/**
	 * Set the max value
	 * @param value : the new maximal value
	 */
	public void setMin(int value){
			this.setMinInt(value);
			this.setMinDouble(value);
			this.minimal = true;
	}
	
	/**
	 * Set the max value
	 * @param value : the new maximal value
	 */
	public void setMin(double value){
		if (this.base == typesEnum.floatType){
			int _intValue = (int) value;
			if (_intValue > value) _intValue--;
			
			this.setMinDouble(value);
			this.setMinInt(_intValue);
		}
		else{
			this.setMin((int)value);
		}
		this.minimal = true;
	}
	
	/**
	 * Set the minimal value
	 * @param minInt minimal integer value as string
	 * @param minDouble minimal float value as string
	 */
	public void setMin(String minInt, String minDouble){
		if ((minInt == null) && (minDouble == null)){
			return;
		}
		else if ((minInt != null) && (minDouble != null)){
			
			double _doubleValue = Double.valueOf(minDouble);
			int _intValue = Integer.valueOf(minInt);
			if (this.base == typesEnum.floatType){
				this.setMin(_doubleValue);
			}
			else if ((this.base == typesEnum.intType) || (this.base == typesEnum.unsigned)){
				this.setMin(_intValue);
			}
			else{
				if (_intValue < _doubleValue) this.setMin(_intValue);
				else this.setMin(_doubleValue);
			}
		}
		else if (minInt == null){
			double _doubleValue = Double.valueOf(minDouble);
			this.setMin(_doubleValue);
		}
		else if (minDouble == null){
			int _intValue = Integer.valueOf(minInt);
			this.setMin(_intValue);
		}
		else{
			System.err.println("Type::setMin(string, string) : Impossible case");
		}
	}
	
	/**
	 * set the string based type length
	 * @param length
	 */
	public void setLenght(long length){
		this.length = length;
	}
	
	/**
	 * set the string based type minimal length
	 * @param minimal length
	 */
	public void setMinLenght(long length){
		this.minLength = length;
	}
	
	/**
	 * set the string based type maximal length
	 * @param maximal length
	 */
	public void setMaxLenght(long length){
		this.maxLength = length;
	}
	
	/**
	 * function indicate if a maximal value was specified
	 * @return true if a maximal value was specified false if not
	 */
	public boolean hasMaximal(){
		return this.maximal;
	}
	
	/**
	 * function indicate if a minimal value was specified
	 * @return true if a minimal value was specified false if not
	 */
	public boolean hasMinimal(){
		return this.minimal;
	}
	
	/**
	 * Add a new possible value to the type
	 * @param value : the new admissible value
	 */
	public void addValue(String value){
		Value _newValue = new Value(this.base, value);
		this.values.add(_newValue);
	}
	
	/**
	 * get the value at the given position
	 * @param position : the position of the value
	 * @return the value found at the given position
	 */
	public Value getValue(int position){
		return this.values.get(position);
	}
	
	/**
	 * Add a new forbidden value to the type
	 * @param value : the new forbidden value
	 */
	public void addForbidden(String value){
		Value _newValue = new Value(this.base, value);
		this.forbidden.add(_newValue);
	}
	
	/**
	 * get the forbidden value at the given position
	 * @param position : the position of the forbidden value
	 * @return the forbidden value found at the given position
	 */
	public Value getForbiden(int position){
		return this.forbidden.get(position);
	}
	
	//static methods
	/**
	 * return the given type as a string
	 * @return the string value of the entry type
	 */
	public static String typeToString(typesEnum type){
		String _resulat = "";
		
		switch(type){
		case intType : _resulat = "integer"; break;
		case unsigned : _resulat = "unsigned"; break;
		case floatType : _resulat = "float"; break;
		case string : _resulat = "string"; break;
		case specified : _resulat = "specified"; break;
		}
		
		return _resulat;
	}
	
	
	public static typesEnum stringToType(String type){
		typesEnum _result;
		
		if (type.equals("integer")){
			_result = Type.typesEnum.intType;
		}
		else if (type.equals("unsigned")){
			_result = Type.typesEnum.unsigned;
		}
		else if (type.equals("float")){
			_result = Type.typesEnum.floatType;
		}
		else if (type.equals("string")){
			_result = Type.typesEnum.string;
		}
		else{
			_result = Type.typesEnum.specified;
		}
		
		
		return _result;
	}
	
	/**
	 * write the configuration file
	 * @param bufferWriter : buffer writer
	 * @param prefix : the prefix
	 */
	public void writeContent(ZBufferedWriter bufferWriter, String prefix) {
		bufferWriter.write(prefix + "<type name=\"" + this.name +"\" base=\"" + getStringBase() +"\"");
		
		
		if (this.base == typesEnum.floatType){
			if (this.hasMinimal()){
				bufferWriter.write(" minDouble=\"" + this.minDouble +"\"");
			}
			
			if (this.hasMaximal()){
				bufferWriter.write(" maxDouble=\"" + this.maxDouble +"\"");
			}
		}
		else if ((this.base == typesEnum.intType) || (this.base == typesEnum.unsigned)){
			if (this.hasMinimal()){
				bufferWriter.write(" minInt=\"" + this.minInt +"\"");
			}
			
			if (this.hasMaximal()){
				bufferWriter.write(" maxInt=\"" + this.maxInt +"\"");
			}
		}
		else if (this.base == typesEnum.string){
			if (this.length > 0){
				bufferWriter.write(" length=\"" + this.length +"\"");
			}
			
			if (this.minLength > 0){
				bufferWriter.write(" minLength=\"" + this.minLength +"\"");
			}
			
			if (this.maxLength > 0){
				bufferWriter.write(" maxLength=\"" + this.maxLength +"\"");
			}
		}
		
		bufferWriter.writeln(">");
		
		if (this.values != null){
			Iterator<Value> _iterator = this.values.iterator();
			Value _value;
			while(_iterator.hasNext()){
				_value = _iterator.next();
				bufferWriter.writeln(prefix +"	<value>"+ _value +"</value>");
			}

		}
		
		if (this.forbidden != null){
			Iterator<Value> _iterator = this.forbidden.iterator();
			Value _value;
			while(_iterator.hasNext()){
				_value = _iterator.next();
				bufferWriter.writeln(prefix +"	<forbidden>"+ _value +"</forbidden>");
			}
		}
		
		bufferWriter.writeln(prefix +"</type>");
		
		
		
			
	}
}
